package cn.com.zzl.util;

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.SheetUtil;
import org.apache.poi.xssf.usermodel.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Description：java用poi读取和创建2003和2007版本Excel
 * Created by 朱正磊 on 2017-05-02 9:18.
 */
public class POIExcelExample {

	/**
	 * @Desc 创建Excel2003
	 * @throws Exception
	 */
	public static void create2003Excel() throws Exception {
		HSSFWorkbook workbook = new HSSFWorkbook();//创建一个Excel2003文档对象

		//HSSFSheet sheet = workbook.createSheet();//创建一个工作表对象（工作表默认名称为Sheet0）
		HSSFSheet sheet = workbook.createSheet("firstSheet");//创建一个工作表对象（工作表名称自定义为firstSheet）

		//一个字符的1/256的宽度作为一个单位，1000即3.9个字符的宽度，10000即39个字符的宽度
		sheet.setColumnWidth(1,10000);//设置第2列的宽度为10000（列从索引0开始）

		HSSFRow row = sheet.createRow(1);//创建一个行对象（第2行，行从索引0开始）
		//行高1像素长度约为0.365mm，列宽1像素长度约为2.25mm，在1024*768下，1CM约为38像素， 1像素约为0.26毫米
		row.setHeightInPoints(38);//设置第2行的行高为38像素

		//创建样式
		HSSFCellStyle cellStyle = workbook.createCellStyle();//创建单元格样式对象

		//设置字体
		HSSFFont font = workbook.createFont();//创建字体对象
		font.setFontHeightInPoints((short) 15);//设置字体大小为15
		font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);//设置为粗体
		font.setFontName("黑体");//设置为黑体字
		cellStyle.setFont(font);//将字体加入到样式对象

		//设置对齐方式
		cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION);//水平居中
		cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直居中

		//设置边框
		cellStyle.setBorderTop(HSSFCellStyle.BORDER_THICK);//顶部边框粗线
		cellStyle.setBorderBottom(HSSFCellStyle.BORDER_DOUBLE);//底部边框双线
		cellStyle.setBorderLeft(HSSFCellStyle.BORDER_MEDIUM);//左边框中等
		cellStyle.setBorderRight(HSSFCellStyle.BORDER_MEDIUM);//右边框中等
		cellStyle.setTopBorderColor(HSSFColor.RED.index);//设置顶部边框颜色为红色

		//格式化日期
		//cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("yyyy-MM-dd HH:mm:ss"));//格式化日期，这种日期格式不起作用！
		//这里的日期格式有限，可能不满足我们的需求，唯一的解决办法就是先将日期转换为字符串，然后再赋值给另外一个字段，展示该字段即可。
		cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));//格式化日期，对格式有特殊要求！
		HSSFCell cell = row.createCell(1);//创建单元格（第2行第2个，索引从0开始）
		cell.setCellValue(new Date());//设置单元格内容为当前日期
		cell.setCellStyle(cellStyle);//单元格应用样式对象

		//文件输出流，如果存在同名文件那么旧文件将会被新文件所覆盖
		//如果没有路径，只有文件名，那么文件生成目录为项目所在目录，即D:\workspace\project\ideaProject\demo
		FileOutputStream out = new FileOutputStream("style_2003_01.xls");//Excel2003，后缀名是xls
		//我们也可以自定义文件生成目录，不过要写清楚文件所在路径，即文件的全路径
		//FileOutputStream out = new FileOutputStream("E:\\style_2003_01.xls");//Excel2003，后缀名是xls
		workbook.write(out);//将Excel2003文档对象写入文件输出流
		out.close();//关闭文件输出流
		workbook.close();
		System.out.println("【创建成功】： office Excel 2003！");

	}

	/**
	 * @Desc 导出Excel2003
	 * @param fileName 工作簿名称（Excel文件全路径）
	 * @param sheetName 工作表名称
	 * @param list 待导出的数据集合
	 * @param fieldMap 类的英文属性和Excel中的中文列名的对应关系
	 * @param theme 主题
	 * @throws Exception
	 */
	public static <T> void exportExcel2003(String fileName, String sheetName,
										   List<T> list, Map<String,String> fieldMap, String theme) throws Exception {
		//定义存放英文字段名和中文字段名的数组（表头）
		String[] enFields = new String[fieldMap.size()];
		String[] cnFields = new String[fieldMap.size()];
		//填充数组（表头）
		int count=0;
		for(Map.Entry<String,String> entry : fieldMap.entrySet()){
			enFields[count] = entry.getKey();//key英文，字段英文名称
			cnFields[count] = entry.getValue();//value中文，字段中文名称
			count++;
		}
		HSSFWorkbook workbook = new HSSFWorkbook();//创建一个Excel2003文档对象
		HSSFSheet sheet = workbook.createSheet(sheetName);//创建一个工作表对象（工作表名称自定义）
		HSSFRow row1 = sheet.createRow(0);//在sheet里创建第一行，参数为行索引(excel的行)，可以是0～65535之间的任何一个
		HSSFCell cell1 = row1.createCell(0);//创建单元格（excel的单元格，参数为列索引，可以是0～255之间的任何一个
		if(theme != null){//工作表有主题
			cell1.setCellValue(theme);//设置单元格内容
			//合并单元格CellRangeAddress构造参数依次表示起始行，截至行，起始列， 截至列
			sheet.addMergedRegion(new CellRangeAddress(0,0,0,3));
			HSSFRow row2 = sheet.createRow(1);//在sheet里创建第二行
			for(int m = 0; m < count; m++){
				HSSFCell cell = row2.createCell(m);
				cell.setCellValue(cnFields[m]);//第二行设置为字段中文名称，即列名（从第一格开始）
			}
			for(int n = 0; n < list.size(); n++){//从第三行开始给单元格设置内容
				HSSFRow row = sheet.createRow(n+2);
				Object object = list.get(n);
				for(int j = 0; j < count; j++){//从每行的第一个单元格开始给单元格设置内容，因为key和value是对应的
					set2CellValue(row,j,enFields,object);
				}
			}
			autoColumnSize(sheet,count);//自动设置列宽
		}else{//工作表没有主题
			for(int m = 0; m < count; m++){
				HSSFCell cell = row1.createCell(m);
				cell.setCellValue(cnFields[m]);//第一行设置为字段中文名称，即列名（从第一格开始）
			}
			for(int n = 0; n < list.size(); n++){//从第二行开始给单元格设置内容
				HSSFRow row = sheet.createRow(n+1);
				Object object = list.get(n);
				for(int j = 0; j < count; j++){//从每行的第一个单元格开始给单元格设置内容，因为key和value是对应的
					set2CellValue(row,j,enFields,object);
				}
			}
			autoColumnSize(sheet,count);//自动设置列宽
		}
		//文件输出流，如果存在同名文件那么旧文件将会被新文件所覆盖
		//如果没有路径，只有文件名，那么文件生成目录为项目所在目录，即D:\workspace\project\ideaProject\demo
		//FileOutputStream out = new FileOutputStream("style_2003_01.xls");//Excel2003，后缀名是xls
		//我们也可以自定义文件生成目录，不过要写清楚文件所在路径，即文件的全路径
		FileOutputStream out = new FileOutputStream(fileName);//Excel2003，后缀名是xls
		workbook.write(out);//将Excel2003文档对象写入文件输出流
		out.close();//关闭文件输出流
		workbook.close();
		System.out.println("【导出成功】： office Excel 2003！");
	}

	/**
	 * @Desc 读取Excel2003
	 * @param file
	 * @return
	 * @throws Exception
	 */
	public static List<List<Object>> read2003Excel(File file) throws Exception {

		List<List<Object>> list = new LinkedList<List<Object>>();//创建链表式list集合，便于插入操作

		HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(file));//创建一个Excel2003文档对象

		HSSFSheet sheet = workbook.getSheetAt(0);//根据索引获取工作簿中的某个工作表，索引从0开始
		//HSSFSheet sheet = workbook.getSheet("firstSheet");//根据工作表的名字获取工作簿中的某个工作表

		Object value ;//对象
		HSSFRow row ;//行对象
		HSSFCell cell ;//单元格对象

		System.out.println("【读取 office Excel 2003】数据内容如下：");

		for(int i = sheet.getFirstRowNum(); i <= sheet.getPhysicalNumberOfRows(); i++){//循环遍历工作表中的行对象
			row = sheet.getRow(i);
			if(row == null){//如果该行不存在（该行可能被删掉了，行号不连续）
				continue;//跳出本次循环，继续下个循环
			}
			List<Object> linked = new LinkedList<Object>();//创建链表式list集合，快捷插入
			for(int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++){//循环遍历行中的单元格
				cell = row.getCell(j);
				if(cell == null){//如果该单元格不存在
					continue;//跳出本次循环，继续下个循环
				}
				DecimalFormat df = new DecimalFormat("0");//数字格式化，取整数部分
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//日期格式化
				DecimalFormat nf = new DecimalFormat("0.00");//数字格式化，取整数部分以及四舍五入保留2位小数
				switch (cell.getCellType()){//仅支持 int，short，char，byte
					case HSSFCell.CELL_TYPE_STRING://字符串类型
						value = cell.getStringCellValue();
						break;
					case HSSFCell.CELL_TYPE_NUMERIC://数字或者日期类型
						if("@".equals(cell.getCellStyle().getDataFormatString())){//单元格格式为自定义的@，不可控，需要手动设置
							value = df.format(cell.getNumericCellValue());//取整数部分
						}else if("General".equals(cell.getCellStyle().getDataFormatString())){//单元格格式为常规格式
							value = nf.format(cell.getNumericCellValue());//取整数部分以及四舍五入保留2位小数
						}else{//单元格格式为日期格式，比如2017-5-3 10:21，对应的自定义格式为 yyyy-m-d h:mm;@
							value = sdf.format(HSSFDateUtil.getJavaDate(cell.getNumericCellValue()));//格式化日期
						}
						break;
					case HSSFCell.CELL_TYPE_BOOLEAN://boolean类型
						value = cell.getBooleanCellValue();
						break;
					case HSSFCell.CELL_TYPE_BLANK://blank类型，比如null，"  "，""
						value = "";
						break;
					default:
						value = cell.toString();
				}
				if(value == null || "".equals(value)){//单元格里面没有内容
					continue;//跳出本次循环，继续下个循环
				}
				linked.add(value);
			}
			list.add(linked);
		}
		workbook.close();
		return list;
	}

	/**
	 * @Desc 创建Excel2007
	 * @throws Exception
	 */
	public static void create2007Excel() throws Exception {
		XSSFWorkbook workbook = new XSSFWorkbook();//创建一个Excel2007文档对象

		//XSSFSheet sheet = workbook.createSheet();//创建一个工作表对象，工作表默认名称为Sheet0
		XSSFSheet sheet = workbook.createSheet("Sheet_one");//创建一个工作表对象，工作表名称自定义为Sheet_one

		//设置列宽，一个字符的1/256的宽度作为一个单位，1000即3.9个字符的宽度，5000即19.5个字符的宽度
		sheet.setColumnWidth(2,5000);//设置第3列的列宽为5000（列索引从0开始）

		//设置行
		XSSFRow row = sheet.createRow(2);//创建一个行对象（第3行，行索引从0开始）
		//在1024*768下，1CM约为38像素， 1像素约为0.26毫米
		row.setHeightInPoints(38);//设置行高为38像素

		//创建样式
		XSSFCellStyle cellStyle = workbook.createCellStyle();//创建单元格样式对象

		//设置字体
		XSSFFont font = workbook.createFont();//创建字体对象
		font.setFontHeightInPoints((short) 15);//设置字体大小为15
		font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//设置为粗体
		font.setFontName("黑体");//设置为黑体字
		cellStyle.setFont(font);//将字体加入到样式对象

		//设置对齐方式
		cellStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER_SELECTION);//水平居中
		cellStyle.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);//垂直居中

		//设置边框
		cellStyle.setBorderTop(XSSFCellStyle.BORDER_THICK);//顶部边框粗线
		cellStyle.setBorderBottom(XSSFCellStyle.BORDER_DOUBLE);//底部边框双线
		cellStyle.setBorderLeft(XSSFCellStyle.BORDER_MEDIUM);//左边框中等
		cellStyle.setBorderRight(XSSFCellStyle.BORDER_MEDIUM);//右边框中等
		cellStyle.setTopBorderColor(HSSFColor.RED.index);//设置顶部边框颜色为红色，XSSFColor没有可设置颜色，这里使用HSSFColor

		//格式化日期，因为XSSFDataFormat没有getBuiltinFormat方法，因此我们要使用HSSFDataFormat来进行日期格式转换
		//cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("yyyy-MM-dd HH:mm:ss"));//格式化日期，这种日期格式不起作用！
		//这里的日期格式有限，可能不满足我们的需求，唯一的解决办法就是先将日期转换为字符串，然后再赋值给另外一个字段，展示该字段即可。
		cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("d-mmm-yy"));//格式化日期，对格式有特殊要求！
		XSSFCell cell = row.createCell(2);//创建单元格（第3行第3个，索引从0开始）
		cell.setCellValue(new Date());//设置单元格内容为当前日期
		cell.setCellStyle(cellStyle);//单元格应用样式对象

		//文件输出流，如果存在同名文件那么旧文件将会被新文件所覆盖
		//如果没有路径，只有文件名，那么文件生成目录为项目所在目录，即D:\workspace\project\ideaProject\demo
		FileOutputStream out = new FileOutputStream("style_2007_02.xlsx");//Excel2007，后缀名是xlsx
		//我们也可以自定义文件生成目录，不过要写清楚文件所在路径，即文件的全路径
		//FileOutputStream out = new FileOutputStream("E:\\style_2007_02.xlsx");//Excel2007，后缀名是xlsx
		workbook.write(out);//将Excel2007文档对象写入文件输出流
		out.close();//关闭文件输出流
		workbook.close();
		System.out.println("【创建成功】： office Excel 2007！");
	}

	/**
	 * @Desc 导出Excel2007
	 * @param fileName 工作簿名称（Excel文件全路径）
	 * @param sheetName 工作表名称
	 * @param list 待导出的数据集合
	 * @param fieldMap 类的英文属性和Excel中的中文列名的对应关系
	 * @param theme 主题
	 * @throws Exception
	 */
	public static <T> void exportExcel2007(String fileName, String sheetName,
										   List<T> list, Map<String,String> fieldMap, String theme) throws Exception {
		//定义存放英文字段名和中文字段名的数组（表头）
		String[] enFields = new String[fieldMap.size()];
		String[] cnFields = new String[fieldMap.size()];
		//填充数组（表头）
		int count=0;
		for(Map.Entry<String,String> entry : fieldMap.entrySet()){
			enFields[count] = entry.getKey();//key英文，字段英文名称
			cnFields[count] = entry.getValue();//value中文，字段中文名称
			count++;
		}
		XSSFWorkbook workbook = new XSSFWorkbook();//创建一个Excel2003文档对象
		XSSFSheet sheet = workbook.createSheet(sheetName);//创建一个工作表对象（工作表名称自定义）
		XSSFRow row1 = sheet.createRow(0);//在sheet里创建第一行，参数为行索引(excel的行)，可以是0～65535之间的任何一个
		XSSFCell cell1 = row1.createCell(0);//创建单元格（excel的单元格，参数为列索引，可以是0～255之间的任何一个
		if(theme != null){//工作表有主题
			cell1.setCellValue(theme);//设置单元格内容
			//合并单元格CellRangeAddress构造参数依次表示起始行，截至行，起始列， 截至列
			sheet.addMergedRegion(new CellRangeAddress(0,0,0,3));
			XSSFRow row2 = sheet.createRow(1);//在sheet里创建第二行
			for(int m = 0; m < count; m++){
				XSSFCell cell = row2.createCell(m);
				cell.setCellValue(cnFields[m]);//第二行设置为字段中文名称，即列名（从第一格开始）
			}
			for(int n = 0; n < list.size(); n++){//从第三行开始给单元格设置内容
				XSSFRow row = sheet.createRow(n+2);
				Object object = list.get(n);
				for(int j = 0; j < count; j++){//从每行的第一个单元格开始给单元格设置内容，因为key和value是对应的
					set2CellValue(row,j,enFields,object);
				}
			}
			autoColumnSize(sheet,count);//自动设置列宽
		}else{//工作表没有主题
			for(int m = 0; m < count; m++){
				XSSFCell cell = row1.createCell(m);
				cell.setCellValue(cnFields[m]);//第一行设置为字段中文名称，即列名（从第一格开始）
			}
			for(int n = 0; n < list.size(); n++){//从第二行开始给单元格设置内容
				XSSFRow row = sheet.createRow(n+1);
				Object object = list.get(n);
				for(int j = 0; j < count; j++){//从每行的第一个单元格开始给单元格设置内容，因为key和value是对应的
					set2CellValue(row,j,enFields,object);
				}
			}
			autoColumnSize(sheet,count);//自动设置列宽
		}
		//文件输出流，如果存在同名文件那么旧文件将会被新文件所覆盖
		//如果没有路径，只有文件名，那么文件生成目录为项目所在目录，即D:\workspace\project\ideaProject\demo
		//FileOutputStream out = new FileOutputStream("style_2007_02.xlsx");//Excel2007，后缀名是xlsx
		//我们也可以自定义文件生成目录，不过要写清楚文件所在路径，即文件的全路径
		FileOutputStream out = new FileOutputStream(fileName);//Excel2007，后缀名是xlsx
		workbook.write(out);//将Excel2007文档对象写入文件输出流
		out.close();//关闭文件输出流
		workbook.close();
		System.out.println("【导出成功】： office Excel 2007！");
	}

	/**
	 * @Desc 读取Excel2007
	 * @param file
	 * @return
	 * @throws Exception
	 */
	public static List<List<Object>> read2007Excel(File file) throws Exception {

		List<List<Object>> list = new LinkedList<List<Object>>();//创建链表式list集合，便于插入操作

		XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(file));//创建一个Excel2003文档对象

		XSSFSheet sheet = workbook.getSheetAt(0);//根据索引获取工作簿中的某个工作表，索引从0开始
		//XSSFSheet sheet = workbook.getSheet("Sheet_one");//根据工作表的名字获取工作簿中的某个工作表

		Object value ;//对象
		XSSFRow row ;//行对象
		XSSFCell cell ;//单元格对象

		System.out.println("【读取 office Excel 2007】数据内容如下：");

		for(int i = sheet.getFirstRowNum(); i <= sheet.getPhysicalNumberOfRows(); i++){//循环遍历工作表中的行对象
			row = sheet.getRow(i);
			if(row == null){//如果该行不存在（该行可能被删掉了，行号不连续）
				continue;//跳出本次循环，继续下个循环
			}
			List<Object> linked = new LinkedList<Object>();//创建链表式list集合，快捷插入
			for(int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++){//循环遍历行中的单元格
				cell = row.getCell(j);
				if(cell == null){//如果该单元格不存在
					continue;//跳出本次循环，继续下个循环
				}
				DecimalFormat df = new DecimalFormat("0");//数字格式化，取整数部分
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//日期格式化
				DecimalFormat nf = new DecimalFormat("0.00");//数字格式化，取整数部分以及四舍五入保留2位小数
				switch (cell.getCellType()){//仅支持 int，short，char，byte
					case XSSFCell.CELL_TYPE_STRING://字符串类型
						value = cell.getStringCellValue();
						break;
					case XSSFCell.CELL_TYPE_NUMERIC://数字或者日期类型
						if("@".equals(cell.getCellStyle().getDataFormatString())){//单元格格式为自定义的@，不可控，需要手动设置
							value = df.format(cell.getNumericCellValue());//取整数部分
						}else if("General".equals(cell.getCellStyle().getDataFormatString())){//单元格格式为常规格式
							value = nf.format(cell.getNumericCellValue());//取整数部分以及四舍五入保留2位小数
						}else{//单元格格式为日期格式，比如2017-5-3 10:21，对应的自定义格式为 yyyy-m-d h:mm;@
							value = sdf.format(HSSFDateUtil.getJavaDate(cell.getNumericCellValue()));//格式化日期
						}
						break;
					case XSSFCell.CELL_TYPE_BOOLEAN://boolean类型
						value = cell.getBooleanCellValue();
						break;
					case XSSFCell.CELL_TYPE_BLANK://blank类型，比如null，"  "，""
						value = "";
						break;
					default:
						value = cell.toString();
				}
				if(value == null || "".equals(value)){//单元格里面没有内容
					continue;//跳出本次循环，继续下个循环
				}
				linked.add(value);
			}
			list.add(linked);
		}
		workbook.close();
		return list;
	}

	/**
	 * @Desc 读取Excel2003和Excel2007
	 * @param file
	 * @return
	 * @throws Exception
	 */
	public static List<List<Object>> readExcel(File file) throws Exception {
		String fileName = file.getName();//getName()获取文件名（含有后缀），不含路径
		String extension = fileName.lastIndexOf(".") == -1 ? "" : fileName
				.substring(fileName.lastIndexOf(".") + 1);//获取文件后缀名
		if("xls".equals(extension)){//Excel2003
			return read2003Excel(file);
		}else if("xlsx".equals(extension)){//Excel2007
			return read2007Excel(file);
		}else{//既不是Excel2003也不是Excel2007
			throw new IOException("不支持的文件类型！");
		}
	}

	/*******************************************【private辅助的私有方法】****************************************/

	/**
	 * @Desc 自动设置列宽（最终方法）
	 * @param sheet 工作表对象
	 * @param column 总列数
	 */
	private static void autoColumnSize(Sheet sheet, int column){
		for(int i = 0; i < column; i++){
			//sheet.setColumnWidth(i,value.toString().length() * 512);可以解决中文问题，但我不怎么会用
			//sheet.autoSizeColumn(i,true);//自动设置列宽，不过对中文不适用
			setAutoColumnSize(sheet,i);//设置自动列宽
		}
	}

	/**
	 * @Desc 设置自动列宽（半成品）
	 * @param sheet 工作表对象
	 * @param column 列号
	 */
	private static void setAutoColumnSize(Sheet sheet, int column){
		//这里的SheetUtil不是我自己写的工具类，而是第三方提供的工具类，需要import
		double width = SheetUtil.getColumnWidth(sheet, column, true);//这里的true表示对合并单元格也同样适用
		//width * 512这里乘以512而不是256是因为要考虑到中文，乘以256适合字母但不适合中文，乘以512会通用
		//sheet.setColumnWidth(column, (int) (width * 512));
		Row row = sheet.getRow(2);//选择第二行是要确保该行数据是数据库数据（具体选择第几行根据实际情况调整）
		Cell cell = row.getCell(column);
		String content = cell.getStringCellValue();
		if(StringUtil.isChinese(content)){//含有中文
			sheet.setColumnWidth(column, (int) (width * 256 * 1.5));//这里的1.5可以根据实际情况进行调整
		}else{//不含有中文
			sheet.setColumnWidth(column, (int) (width * 256 * 1.1));//这里的1.1可以根据实际情况进行调整
		}
	}

	/**
	 * @Desc 给单元格赋值
	 * @param row 行对象
	 * @param j 列号
	 * @param enFields 字段英文名称
	 * @param object 泛型T
	 * @throws Exception
	 */
	private static void set2CellValue(Row row, int j, String enFields[], Object object) throws Exception {
		Cell cell = row.createCell(j);
		Object objValue = getFieldValueByNameSequence(enFields[j], object);
		String fieldValue = getFieldValue(objValue);
		cell.setCellValue(fieldValue);
	}

	/**
	 * @Desc 根据带路径或不带路径的属性名获取属性值，
	 * 			即接受简单属性名，如userName等，又接受带路径的属性名，如student.department.name等
	 * @param fieldNameSequence 带路径的属性名或简单属性名
	 * @param o 对象
	 * @return 属性值
	 * @throws Exception
	 */
	private static Object getFieldValueByNameSequence(String fieldNameSequence, Object o) throws Exception{
		Object value ;
		String[] attributes = fieldNameSequence.split("\\.");//将fieldNameSequence根据"."进行拆分
		if(attributes.length == 1){
			value = getFieldValueByName(fieldNameSequence, o);
		}else{
			//根据属性名获取属性对象
			Object fieldObj = getFieldValueByName(attributes[0], o);
			//说明：其中indexOf(".")获取的是.首次出现的位置
			String subFieldNameSequence = fieldNameSequence.substring(fieldNameSequence.indexOf(".") + 1);
			value = getFieldValueByNameSequence(subFieldNameSequence, fieldObj);
		}
		return value;
	}

	/**
	 * @Desc 根据字段名获取字段值
	 * @param fieldName 字段名
	 * @param o 对象
	 * @return 返回字段值
	 * @throws Exception
	 */
	private static Object getFieldValueByName(String fieldName, Object o) throws Exception{
		//写法一：
		Field field = getFieldByName(fieldName, o.getClass());
		if(field != null){
			//如果取得的Field是private的,那么就要调用setAccessible(true),否则会报IllegalAccessException
			field.setAccessible(true);
			return field.get(o);//获取字段值
		}else{
			throw new ExcelException(o.getClass().getSimpleName() + "类不存在字段名 " + fieldName);
		}
		//return null;//这行代码不能写，因为不会执行到本行代码，Unreachable statement

		//写法二：
		/*Object value;
		Field field = getFieldByName(fieldName, o.getClass());
		if(field != null){
			field.setAccessible(true);
			value = field.get(o);
		}else{
			throw new ExcelException(o.getClass().getSimpleName() + "类不存在字段名 " + fieldName);
		}
		return value;*/
	}

	/**
	 * @Desc 根据字段名获取字段
	 * @param fieldName 字段名
	 * @param clazz 包含该字段的类
	 * @return 返回字段
	 */
	private static Field getFieldByName(String fieldName, Class<?> clazz){
		//拿到本类的所有字段
		Field[] selfFields = clazz.getDeclaredFields();

		//如果本类中存在该字段，则返回
		for(Field field : selfFields){
			if(field.getName().equals(fieldName)){
				return field;
			}
		}

		//否则，查看父类中是否存在此字段，如果有则返回
		Class<?> superClazz = clazz.getSuperclass();
		if(superClazz != null  &&  superClazz != Object.class){
			return getFieldByName(fieldName, superClazz);
		}

		//如果本类和父类都没有，则返回空
		return null;
	}

	/**
	 * @Desc 根据属性对象获取属性的值
	 * @param objValue
	 * @return
	 */
	private static String getFieldValue(Object objValue){
		String fieldValue;
		if(objValue instanceof Date){//日期时间
			fieldValue = CalendarUtil.dateToString((Date)objValue,"yyyy-MM-dd HH:mm:ss");
		}else{
			fieldValue = objValue == null ? "" : objValue.toString();
		}
		return fieldValue;
	}

	/*******************************************【private辅助的私有方法】****************************************/

	/**
	 * @Desc 测试创建和读取Excel
	 * @param args
	 */
	public static void main(String[] args) throws Exception {

		//create2003Excel();//创建Excel2003
		//create2007Excel();//创建Excel2007

		//读取 office Excel 2003
		/*String path2003 = System.getProperty("user.dir")
				+ System.getProperty("file.separator")//获取项目文件目录，D:\workspace\project\ideaProject\demo
				+ "style_2003_01.xls";
		System.out.println(path2003);//D:\workspace\project\ideaProject\demo\style_2003_01.xls*/
		String path2003 = "E:\\style_2003_02.xls";
		File f2003 = new File(path2003);
		List<List<Object>> list1 = readExcel(f2003);
		System.out.println(list1.size());
		System.out.println(list1.get(1).get(1));
		System.out.println(list1.get(2).get(1));
		System.out.println(list1.get(3).get(1));
		System.out.println(list1.get(4).get(1));
		System.out.println(list1.get(5).get(1));

		//读取 office Excel 2007
		/*String path2007 = System.getProperty("user.dir")
				+ System.getProperty("file.separator")//获取项目文件目录，D:\workspace\project\ideaProject\demo
				+ "style_2007_02.xls";
		System.out.println(path2007);//D:\workspace\project\ideaProject\demo\style_2007_02.xls*/
		String path2007 = "E:\\style_2007_02.xlsx";
		File f2007 = new File(path2007);
		List<List<Object>> list2 = readExcel(f2007);
		System.out.println(list2.size());
		System.out.println(list2.get(1).get(1));
		System.out.println(list2.get(2).get(1));
		System.out.println(list2.get(3).get(1));
		System.out.println(list2.get(4).get(1));
		System.out.println(list2.get(5).get(1));
		System.out.println(list2.get(6).get(1));
		System.out.println(list2.get(7).get(1));
		System.out.println(list2.get(8).get(1));
	}

}
